package open.tech.dawn.rest.common.runner;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import open.tech.dawn.core.error.ServiceException;
import open.tech.dawn.core.util.SpringContextHolder;
import open.tech.dawn.rest.common.Const;
import open.tech.dawn.rest.common.annot.Menu;
import open.tech.dawn.rest.common.entity.MenuEntity;
import open.tech.dawn.rest.module.system.enums.MenuStatus;
import open.tech.dawn.rest.module.system.model.SysMenu;
import open.tech.dawn.rest.module.system.model.SysRelation;
import open.tech.dawn.rest.module.system.service.ISysMenuService;
import open.tech.dawn.rest.module.system.service.ISysRelationService;
import open.tech.dawn.web.util.ToolUtil;
import org.springframework.aop.support.AopUtils;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

@Component
@Slf4j
public class MenuBuildRunner implements ApplicationRunner {

    @Resource
    private ISysMenuService sysMenuService;

    @Resource
    private ISysRelationService sysRelationService;


    private void resolveMethod(Class<?> tClass, Map<String, MenuEntity> menuEntityMap) {
        Method[] publicMethods = ClassUtil.getPublicMethods(tClass);
        if (publicMethods != null) {
            for (Method method : publicMethods) {
                Menu menu = method.getAnnotation(Menu.class);
                if (menu == null)
                    continue;
                loadMenu(menu, menuEntityMap, method::getAnnotation);
            }
        }
    }

    private interface AnnotationGetter {
        Annotation getAnnotation(Class<? extends RequestMapping> tClass);
    }

    private void loadMenu(Menu menu, Map<String, MenuEntity> menuEntityMap, AnnotationGetter annotationGetter) {
        if (menu == null) {
            return;
        }
        if (menuEntityMap.containsKey(menu.code())) {
            throw new RuntimeException(menu.code() + "菜单编码存在多个");
        }
        MenuEntity menuEntity = new MenuEntity();
        menuEntity.setName(menu.name());
        menuEntity.setOrder(menu.order());
        menuEntity.setPage(menu.page());
        menuEntity.setCode(menu.code());
        menuEntity.setHref(menu.url());
        menuEntity.setIcon(menu.icon());
        menuEntity.setParentCode(menu.parentCode());
        if (StrUtil.isBlank(menuEntity.getUrl())) {
            Class<? extends RequestMapping>[] tClasses = new Class[]{
                    RequestMapping.class, GetMapping.class, PostMapping.class
                    , DeleteMapping.class};
            for (Class<? extends RequestMapping> tClass : tClasses) {
                Annotation annotation = annotationGetter.getAnnotation(tClass);
                if (annotation != null) {
                    RequestMapping requestMapping = (RequestMapping) annotation;
                    String[] value = requestMapping.value();
                    if (value.length > 0) {
                        menuEntity.setUrl(requestMapping.value()[0]);
                    }
                }
            }
            menuEntityMap.put(menuEntity.getCode(), menuEntity);
        }

    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("开始加载菜单");
        ApplicationContext applicationContext = SpringContextHolder.getApplicationContext();
        Map<String, MenuEntity> menuEntityMap = new HashMap<>();
        Map<String, Object> beansWithAnnotation =
                applicationContext.getBeansWithAnnotation(RestController.class);
        for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
            Class<?> aClass = AopUtils.getTargetClass(entry.getValue());
            Menu menu = aClass.getAnnotation(Menu.class);
            if (menu == null) {
                continue;
            }
            loadMenu(menu,
                    menuEntityMap,
                    aClass::getAnnotation);
            resolveMethod(aClass, menuEntityMap);
        }
        for (Map.Entry<String, MenuEntity> entry : menuEntityMap.entrySet()) {
            MenuEntity menuEntity = entry.getValue();
            String parentCode = menuEntity.getParentCode();
            MenuEntity menuEntity1 = menuEntityMap.get(parentCode);
            if (menuEntity1 != null) {
                menuEntity1.getChildren().add(menuEntity);
            }
        }
        List<MenuEntity> list = new ArrayList<>();
        for (MenuEntity menuEntity : menuEntityMap.values()) {
            if (!menuEntityMap.containsKey(menuEntity.getParentCode())) {
                list.add(menuEntity);
            }
        }
        saveOrUpdate(list, "");
        List<SysMenu> list1 = sysMenuService.list();
        for (SysMenu sysMenu : list1) {
            log.info(JSONUtil.toJsonStr(sysMenu));
        }
        log.info("结束加载菜单");
    }

    private void saveOrUpdate(List<MenuEntity> list, String parentUrl) {
        for (MenuEntity menuEntity : list) {
            SysMenu query = new SysMenu();
            query.setCode(menuEntity.getCode());
            SysMenu sysMenu = sysMenuService.getOne(new QueryWrapper<>(query));
            if (sysMenu == null) {
                sysMenu = new SysMenu();
            }
            sysMenu.setName(menuEntity.getName());
            sysMenu.setPcode(menuEntity.getParentCode());
            sysMenu.setCode(menuEntity.getCode());
            sysMenu.setIsmenu(menuEntity.isPage() ? 1 : 0);
            sysMenu.setNum(menuEntity.getOrder());
            sysMenu.setIcon(menuEntity.getIcon());
            sysMenu.setStatus(MenuStatus.ENABLE.getCode());
            String url = "#".equals(parentUrl) ? "" : parentUrl;
            if (StrUtil.isNotEmpty(menuEntity.getUrl())) {
                if (menuEntity.getUrl().startsWith("/")) {
                    url += menuEntity.getUrl();
                } else {
                    url += "/" + menuEntity.getUrl();
                }
            }
            if (StrUtil.isEmpty(url)) {
                url = "#";
            } else {
                url = url.startsWith("/") ? url : "/" + url;
            }
            url = url.replaceAll("\\{[^/]+\\}", "*");
            sysMenu.setUrl(StrUtil.isNotEmpty(menuEntity.getHref()) ? menuEntity.getHref() : url);
            sysMenu.setIsopen(0);
            menuSetPcode(sysMenu);
            if (sysMenu.getId() == null) {
                sysMenuService.save(sysMenu);
            }
            SysRelation sysRelation = new SysRelation();
            sysRelation.setRoleid(Integer.valueOf(Const.ADMIN_ROLE_ID));
            sysRelation.setMenuid(sysMenu.getId());
            if (sysRelationService.count(new QueryWrapper<>(sysRelation)) == 0) {
                sysRelationService.save(sysRelation);
            }
            if (!menuEntity.getChildren().isEmpty()) {
                saveOrUpdate(menuEntity.getChildren(), url);
            }
        }
    }

    /**
     * 根据请求的父级菜单编号设置pcode和层级
     */
    private void menuSetPcode(SysMenu menu) {
        if (ToolUtil.isEmpty(menu.getPcode()) || menu.getPcode().equals("0")) {
            menu.setPcode("0");
            menu.setPcodes("[0],");
            menu.setLevels(1);
        } else {
            SysMenu query = new SysMenu();
            query.setCode(menu.getPcode());
            SysMenu pMenu = sysMenuService.getOne(new QueryWrapper<>(query));
            Integer pLevels = pMenu.getLevels();
            menu.setPcode(pMenu.getCode());

            //如果编号和父编号一致会导致无限递归
            if (menu.getCode().equals(menu.getPcode())) {
                throw new ServiceException("菜单无上级");
            }

            menu.setLevels(pLevels + 1);
            menu.setPcodes(pMenu.getPcodes() + "[" + pMenu.getCode() + "],");
        }
    }
}
